﻿using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using PublicClass;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PublicTool1
{
    class SameEntXData
    {
        public bool IsUsed { get; set; }
        public SameEntXData()
        {
            IsUsed = false;
        }
    }
    class OneBlkData
    {
        public List<NodeEntData> OneBlkNode = new List<NodeEntData>();
        public List<OneBlkData> OtherSameBlkData = new List<OneBlkData>();

        public void HideEnt(bool bHide = true, Transaction ts = null)
        {
            bool bNew = false;
            if (null == ts)
            {
                bNew = true;
                ts = CadUnits.StartTransaction();
            }

            foreach (var item in OneBlkNode)
            {
                var obj = ts.GetObject(item.Entity.ObjectId, OpenMode.ForWrite);
                if (obj == null)
                    continue;

                Entity ent = obj as Entity;
                if (ent != null)
                {
                    ent.Visible = !bHide;
                }
            }

            foreach (var item in OtherSameBlkData)
            {
                item.HideEnt(bHide, ts);
            }

            if (bNew)
            {
                ts.Commit();
                ts.Dispose();
            }


        }

        public void SetEntUsed()
        {
            foreach (var item in OneBlkNode)
            {
                (item.ExData as SameEntXData).IsUsed = true;
            }
        }

        List<Entity> CloneAllEnt()
        {
            List<Entity> lst = new List<Entity>();
            foreach (var item in OneBlkNode)
            {
                var ent = item.Entity.Clone() as Entity;
                ent.Visible = true;
                lst.Add(ent);
            }

            return lst;
        }

        List<ObjectId> GetAllEnts()
        {
            List<ObjectId> lst = new List<ObjectId>();
            foreach (var item in OneBlkNode)
            {
                lst.Add(item.Entity.ObjectId);
            }
            return lst;
        }

        Point3d CenterPt()
        {
            Extents3d exAll = new Extents3d();
            foreach (var item in OneBlkNode)
            {
                exAll.AddExtents(item.Extents3d);
            }
            return exAll.GetCenterPoint();
        }

        Extents3d AllExtent()
        {
            Extents3d ex = new Extents3d();
            foreach (var item in OneBlkNode)
            {
                ex.AddExtents(item.Extents3d);
            }

            return ex;
        }

        /// <summary>
        /// 获取相对外包
        /// </summary>
        /// <returns></returns>
        public Extents3d GetRelativeExtent(NodeEntData source, NodeEntData sourceRelative)
        {
            Extents3d ex = AllExtent();
            Extents3d exSource = source.Extents3d;
            Point3d ptCenterSource = exSource.GetCenterPoint();
            Point3d ptRelative = sourceRelative.Extents3d.GetCenterPoint();

            Matrix3d mat = Matrix3d.Displacement(ptRelative - ptCenterSource);
            ex.TransformBy(mat);

            return ex;
        }

        public bool CreateBlk(string strBlkName, Transaction ts = null)
        {
            bool bNew = false;
            if (null == ts)
            {
                bNew = true;
                ts = CadUnits.StartTransaction();
            }

            ObjectId idBlkRecord = CadUnits.CreatBlk(ts, strBlkName, CloneAllEnt());

            if (idBlkRecord.IsNull)
                HideEnt(false, ts);
            else
            {
                BlockReference blk = new BlockReference(CenterPt(), idBlkRecord);
                CadUnits.AppendEntity(ts, blk);
                CadUnits.EraseEnt(GetAllEnts(), ts);

                foreach (var item in OtherSameBlkData)
                {
                    blk = new BlockReference(item.CenterPt(), idBlkRecord);
                    CadUnits.AppendEntity(ts, blk);
                    CadUnits.EraseEnt(item.GetAllEnts(), ts);
                }
            }

            if (bNew)
            {
                ts.Commit();
                ts.Dispose();
            }

            return true;
        }
    }
    public class SameEntsToBlk
    {       
        private QuadThree QuadThree { get; set; }
        private List<ObjectId> AllEnts { get; set; }

        private Dictionary<ObjectId, NodeEntData> ObjToEntNode { get; set; }

        private List<OneBlkData> AllBlk { get; set; }
        public SameEntsToBlk()
        {
            QuadThree = null;
            AllEnts = null;
            AllBlk = new List<OneBlkData>();
            ObjToEntNode = new Dictionary<ObjectId, NodeEntData>();
        }

        public bool Go()
        {
            List<ObjectId> lstAll = UserSelectEnts("\n请选择要转换的所有实体(会排除图块)");
            if (lstAll.Count == 0)
                return false;

            InitThree(lstAll);

            return true;
        }
        private NodeEntData FindNode(ObjectId id)
        {
            NodeEntData nodeEnt = null;
            ObjToEntNode.TryGetValue(id, out nodeEnt);

            return nodeEnt;
        }
        public bool OneGroupDo()
        {
            List<ObjectId> lst = UserSelectEnts("\n请选择样例组实体");
            if (lst.Count == 0)
                return false;
            OneBlkData blkData = new OneBlkData();
            using (Transaction ts = CadUnits.StartTransaction())
            {
                foreach (var item in lst)
                {
                    var obj = ts.GetObject(item, OpenMode.ForRead);
                    if (obj == null)
                        continue;

                    Entity ent = obj as Entity;
                    if (ent != null)
                    {
                        if (ent is BlockReference)
                            continue;

                        NodeEntData entNode = FindNode(item);
                        if (null == entNode)
                        {
                            entNode = new NodeEntData(ent);
                            entNode.ExData = new SameEntXData();
                            QuadThree.AddEntity(entNode);
                        }

                        (entNode.ExData as SameEntXData).IsUsed = true;

                        blkData.OneBlkNode.Add(entNode);
                    }
                }
            }

            if (blkData.OneBlkNode.Count == 0)
                return false;

            SelectAllSample(blkData);
            blkData.HideEnt();
            AllBlk.Add(blkData);


            return true;
        }
        public bool DoBlk()
        {
            // 每个分组组成一个图块
            using (Transaction ts = CadUnits.StartTransaction())
            {
                foreach (var item in AllBlk)
                {
                    item.CreateBlk(Guid.NewGuid().ToString(), ts);
                }

                ts.Commit();
            }
            //

            return true;
        }
        bool SelectAllSample(OneBlkData blkData)
        {
            if (blkData.OneBlkNode.Count == 0)
                return false;

            NodeEntData nodeEnt = blkData.OneBlkNode[0]; // 种子
            foreach (var obj in ObjToEntNode)
            {
                NodeEntData nodeFor = obj.Value;
                SameEntXData dataX = nodeFor.ExData as SameEntXData;
                if (dataX.IsUsed)
                    continue;
                if (IsSameDataEnt(nodeEnt, nodeFor))
                {
                    // 根据相对位置信息找到合适的外包进行搜索
                    Extents3d exRelative = blkData.GetRelativeExtent(nodeEnt, nodeFor);

                    // 用相对外包搜索实体
                    List<NodeEntData> lst = QuadThree.RectSearch(exRelative.MinPoint, exRelative.MaxPoint);

                    List<NodeEntData> lstSearch = new List<NodeEntData>();
                    // 过滤掉不完全在该外包内的
                    foreach (var item in lst)
                    {
                        if (exRelative.ContainsXY(item.Extents3d.MinPoint) && exRelative.ContainsXY(item.Extents3d.MaxPoint))
                        {
                            bool bAdd = true;
                            foreach (var itemOld in lstSearch)
                            {
                                if (IsSameDataEnt(item, itemOld) && item.Extents3d == itemOld.Extents3d)
                                {
                                    bAdd = false;
                                    break;
                                }
                            }
                            if (bAdd)
                                lstSearch.Add(item);
                        }
                    }

                    // 确定找到了
                    if (lstSearch.Count == blkData.OneBlkNode.Count)
                    {
                        OneBlkData blkSame = new OneBlkData();
                        blkSame.OneBlkNode = lstSearch;
                        blkSame.SetEntUsed();
                        blkData.OtherSameBlkData.Add(blkSame);
                    }
                }
            }

            return true;
        }
        static Tolerance TolVec = new Tolerance(0.000001, 0.000001);
        bool IsSameDataEnt(NodeEntData left, NodeEntData right)
        {
            if (null == left || null == right)
                return false;
            if (left == right)
                return false;

            if (left.Entity.GetRXClass().Name != right.Entity.GetRXClass().Name)
                return false;
            // 外包相同
            if (!left.Extents3d.IsSameSize(right.Extents3d))
            {
                return false;
            }

            Entity ent = left.Entity;
            if (ent is Circle)
            {
                Circle cLeft = (Circle)ent;
                Circle cRight = (Circle)right.Entity;

                if (!PublicNoCad.EqualDouble(cLeft.Radius, cLeft.Radius))
                    return false;
            }
            else if (ent is DBPoint)
            {
                DBPoint ptLeft = (DBPoint)ent;
                DBPoint ptRight = (DBPoint)right.Entity;

                if (ptLeft.LayerId != ptRight.LayerId)
                    return false;
            }
            else if (ent is Curve)
            {
                Curve curveLeft = (Curve)ent;
                Curve curveRight = (Curve)right.Entity;

                double dDistParam = curveLeft.GetDistanceAtParameter(curveLeft.EndParam)- curveLeft.GetDistanceAtParameter(curveLeft.StartParam);
                double dDistParam1 = curveLeft.GetDistanceAtParameter(curveLeft.EndParam)- curveLeft.GetDistanceAtParameter(curveLeft.StartParam);

                if (!PublicNoCad.EqualDouble(dDistParam, dDistParam1))
                    return false;

                Vector3d vecDirLeft = curveLeft.EndPoint - curveLeft.StartPoint;
                Vector3d vecDirRight = curveRight.EndPoint - curveRight.StartPoint;
                if (!vecDirLeft.IsEqualTo(vecDirRight, TolVec))
                    return false;
            }
            else if (ent is DBText)
            {
                DBText tLeft = (DBText)ent;
                DBText tRight = (DBText)right.Entity;

                if (tLeft.TextString != tRight.TextString)
                    return false;
            }
            else if (ent is Hatch)
            {
                //                 Hatch hLeft = (Hatch)ent;
                //                 Hatch hRight = (Hatch)right.Entity;
                // 
                //                 if (!PublicNoCad.EqualDouble(hLeft.Area, hRight.Area))
                //                     return false;
            }

            return true;
        }
        private bool InitThree(List<ObjectId> lstAll)
        {
            AllEnts = lstAll;
            List<NodeEntData> lstEnt = new List<NodeEntData>();
            Extents3d exAll = new Extents3d();
            using (Transaction ts = CadUnits.StartTransaction())
            {
                foreach (var item in lstAll)
                {
                    var obj = ts.GetObject(item, OpenMode.ForRead);
                    if (obj == null)
                        continue;

                    Entity ent = obj as Entity;
                    if (ent != null)
                    {
                        if (ent is BlockReference)
                            continue;
                        var entNode = new NodeEntData(ent);
                        entNode.ExData = new SameEntXData();
                        ObjToEntNode.Add(item, entNode);
                        lstEnt.Add(entNode);
                        exAll.AddExtents(entNode.Extents3d);
                    }
                }
            }
            // 四叉树用于加速搜索
            QuadThree = new QuadThree(exAll);
            for (int i = 0; i < lstEnt.Count; i++)
            {
                QuadThree.AddEntity(lstEnt[i]);
            }

            return true;
        }
        static private List<ObjectId> UserSelectEnts(string prompt)
        {
            List<ObjectId> lst = new List<ObjectId>();
            PromptSelectionOptions opt = new PromptSelectionOptions();
            opt.MessageForAdding = prompt;

            PromptSelectionResult res = CadUnits.CurEdit().GetSelection(opt);
            if (res.Status != PromptStatus.OK)
                return lst;

            lst = new List<ObjectId>(res.Value.GetObjectIds());

            return lst;
        }
    }
}
